#include "matrix.h"
#include <QDebug>

Matrix::Matrix(int nRow, int nCol) :
    m_r(nRow), m_c(nCol), m_data(NULL)
{
    m_data = new qreal[m_r*m_c];
    fill(0.0);
}

Matrix::Matrix(const Matrix& m) :
    m_r(m.nRows()), m_c(m.nColumns()), m_data(NULL)
{
    m_data = new qreal[m_r*m_c];
    for(int i = 0; i < m_r; ++i)
    {
        for(int j = 0; j < m_c; ++j)
        {
            m_data[i*m_c + j] = m(i,j);
        }
    }
}

Matrix::~Matrix()
{
    if(m_data != NULL)
        delete [] m_data;
    m_data = NULL;
}

int Matrix::nRows() const
{
    return m_r;
}

int Matrix::nColumns() const
{
    return m_c;
}

const qreal& Matrix::operator() ( int row, int column ) const
{
    Q_ASSERT(row < m_r && row >= 0 && column >= 0 && column < m_c );

    return m_data[row*m_c + column];
}

qreal&	Matrix::operator() ( int row, int column )
{
    Q_ASSERT(row < m_r && row >= 0 && column >= 0 && column < m_c );

    return m_data[row*m_c + column];
}

void Matrix::fill(qreal v)
{
    for(int i = 0; i < m_r; ++i)
    {
        for(int j = 0; j < m_c; ++j)
        {
            m_data[i*m_c + j] = v;
        }
    }
}

void Matrix::loadIdentity()
{
    Q_ASSERT( m_r == m_c);
    fill(0.0);
    int minDim = m_r < m_c? m_r : m_c;
    for(int i = 0; i < minDim; ++i)
        m_data[i*m_c + i] = 1.0;
}

const Matrix& Matrix::operator=(const Matrix& m)
{
    delete [] m_data;
    m_r = m.nRows();
    m_c = m.nColumns();
    m_data = new qreal[m_r*m_c];
    for(int i = 0; i < m_r; ++i)
        for(int j = 0; j < m_c; ++j)
            (*this)(i,j) = m(i,j);
}

Matrix operator*(const Matrix& m1, const Matrix& m2)
{
    Q_ASSERT( m1.nColumns() == m2.nRows());

    Matrix ret(m1.nRows(), m2.nColumns());
    for(int i = 0; i < m1.nRows(); ++i)
    {
        for(int j = 0; j <m2.nColumns(); ++j)
        {
            qreal s = 0.0;
            for(int k = 0; k < m1.nColumns(); ++k)
                s+= m1(i,k) * m2(k,j);
            ret(i,j) = s;
        }
    }

    return ret;
}

QDebug operator<<(QDebug dbg, const Matrix &matrix)
 {
    dbg.nospace() << "Matrix:\n";
    for(int i = 0; i < matrix.nRows(); ++i)
    {
        dbg.nospace() << "\t" << "[ ";
        for(int j = 0; j < matrix.nColumns(); ++j)
            dbg.nospace() << matrix(i,j) << " ";
        dbg.nospace() << "]\n";
    }
    return dbg.space();
 }

QDataStream & operator<< ( QDataStream & stream, const Matrix & matrix )
{
    for(int i = 0; i < matrix.nRows(); ++i)
    {
        for(int j = 0; j < matrix.nColumns(); ++j)
        {
            stream << matrix(i,j);
        }
    }
    return stream;
}

QDataStream & operator>> ( QDataStream & stream, Matrix& matrix )
{
    for(int i = 0; i < matrix.nRows(); ++i)
    {
        for(int j = 0; j < matrix.nColumns(); ++j)
        {
            qreal a;
            stream >> a;
            matrix(i,j) = a;
        }
    }
    return stream;
}
